Skip to content

🤖 feat: support Coder path-app iframe embedding#3194

Merged
ibetitsmike merged 4 commits intomainfrom
mike/mux-iframe-qmab
Apr 26, 2026
Merged

🤖 feat: support Coder path-app iframe embedding#3194
ibetitsmike merged 4 commits intomainfrom
mike/mux-iframe-qmab

Conversation

@ibetitsmike
Copy link
Copy Markdown
Contributor

@ibetitsmike ibetitsmike commented Apr 26, 2026

Mux is working on behalf of Mike.

Summary

Adds path-app iframe support for Mux when it is served behind a Coder app prefix such as /@user/workspace/apps/mux/. Built assets, static web app metadata, API routes, docs, auth redirects, router paths, and WebSocket upgrades now resolve through the detected public prefix instead of assuming origin root.

Background

Mux's production HTML injected <base href="/" />, which caused relative Vite bundle paths to resolve from origin root. That breaks when Coder serves Mux under a path prefix inside an iframe. The backend also matched HTTP and WebSocket routes only at root paths, so deployments where the proxy forwards prefixed paths could miss /api, /orpc, /auth, and WS upgrade routes.

Implementation

  • Added src/common/appProxyBasePath.ts to centralize Coder app-proxy path parsing and stripping.
  • Changed the server to compute <base href> per request from forwarded headers or direct prefixed paths, with validated and escaped path values.
  • Added early server middleware that strips a detected app-proxy prefix from HTTP route matching while preserving the public base path for generated URLs.
  • Normalized WebSocket upgrade paths before matching /orpc/ws, /browser/ws, and /desktop/ws.
  • Made Scalar docs, OpenAPI server URLs, and OAuth redirect URLs prefix-aware.
  • Updated browser routing and URL helpers so MemoryRouter sync, service worker registration, terminal popouts, and frontend asset resolution preserve the prefix.
  • Converted manifest, favicon, apple-touch-icon, PWA icon, and PWA shortcut URLs to relative app-root paths so they respect the injected <base href> and manifest location.

Validation

Before opening the PR:

  • make static-check
  • bun test src/common/appProxyBasePath.test.ts src/browser/utils/backendBaseUrl.test.ts src/node/orpc/server.test.ts
  • make typecheck
  • make lint
  • Production build and dist/cli/index.js server --no-auth smoke checks:
    • Root request serves <base href="/" />.
    • Prefixed request serves <base href="/@u/ws/apps/mux/" />.
    • Sequential prefixed requests for different tenants get distinct base href values.
    • Prefixed bundle, manifest, favicon, font, and icon requests all return 200 under the prefix.
    • Prefixed /api/docs emits /@u/ws/apps/mux/api/spec.json.
  • agent-browser iframe smoke:
    • Loaded a wrapper page containing an iframe pointed at /@u/ws/apps/mux/.
    • Verified the Mux provider onboarding dialog renders inside the iframe.
    • Captured HAR showing the app's HTML, JS, CSS, manifest, font, favicon, and icon requests resolve under /@u/ws/apps/mux/.

After Codex approval on head 52f9524bf4dbd6e1bea2d6eaab953b09b3b8e908:

  • bun test src/common/appProxyBasePath.test.ts src/browser/utils/backendBaseUrl.test.ts src/node/orpc/server.test.ts
  • make static-check
  • make build
  • Production curl smoke on a fresh dist/cli/index.js server --no-auth:
    • Root base href: <base href="/" />.
    • Prefix base href: <base href="/@u/ws/apps/mux/" />.
    • Alternate prefix base href: <base href="/@a/b/apps/mux/" />.
    • Manifest shortcut URL: ./?action=new.
    • Manifest icon URL: icon-192.png.
    • Prefixed /api/spec.json: 200.
    • Prefixed /api/docs spec URL: /@u/ws/apps/mux/api/spec.json.
    • Prefixed main JS asset: 200.
  • agent-browser root and prefix smoke:
    • Root app loads and registers service worker at root scope.
    • Prefixed app loads and registers service worker at /@u/ws/apps/mux/ scope.
    • document.baseURI, manifest href, and favicon href all resolve under /@u/ws/apps/mux/.
  • agent-browser iframe smoke:
    • Wrapper page loaded an iframe pointed at /@u/ws/apps/mux/.
    • Provider onboarding dialog rendered inside the iframe.
    • HAR showed no Mux app requests escaping to origin root.

Artifacts from local dogfood are under dogfood-output/mux-path-app/.

Review status

Codex initially flagged the PWA shortcut URL. The shortcut now uses ./?action=new, the review thread was resolved, and Codex approved the updated head. Required checks passed.

Risks

The main risk is route rewriting affecting root-hosted deployments or normal app routes that contain /apps/. The parser is anchored to Coder-shaped path-app prefixes, and tests cover root mode, direct-prefixed mode, forwarded-header mode, non-prefix /apps/ paths, sequential tenant prefixes, origin validation, WebSocket upgrade normalization, docs URLs, and OAuth URL generation.


Generated with mux • Model: openai:gpt-5.5 • Thinking: xhigh • Cost: $56.00

Audit:
- backendBaseUrl now re-exports the shared parser.
- RouterContext and App preserve the cached app-proxy prefix on browser history writes.
- main.tsx and terminal popouts resolve service-worker.js and terminal.html through document.baseURI with the cached prefix.
- terminal.html uses a relative terminal window module script so it stays under the proxy base path.
- Grep found no raw browser-runtime /api, /auth, /orpc, /service-worker.js, or /terminal.html literals outside stories after the changes.

Validation:
- bun test src/common/appProxyBasePath.test.ts src/browser/utils/backendBaseUrl.test.ts
- make typecheck
Support Coder app-proxy base paths across HTTP routing, WebSocket upgrades, generated SPA base hrefs, Scalar docs, session cookie paths, and OAuth URLs.

Validation:

- bun test src/node/orpc/server.test.ts src/browser/utils/backendBaseUrl.test.ts src/common/appProxyBasePath.test.ts

- make typecheck

- make lint
Use relative manifest and icon URLs so the server-injected base href keeps Coder path-app prefixes for HTML, manifest, and dynamic favicon updates.
@ibetitsmike
Copy link
Copy Markdown
Contributor Author

@codex review

Mux is working on behalf of Mike.

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 37af608cd4

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread public/manifest.json Outdated
@ibetitsmike
Copy link
Copy Markdown
Contributor Author

@codex review

Mux is working on behalf of Mike.

@chatgpt-codex-connector
Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Keep it up!

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@ibetitsmike ibetitsmike merged commit f2a029d into main Apr 26, 2026
24 checks passed
@ibetitsmike ibetitsmike deleted the mike/mux-iframe-qmab branch April 26, 2026 09:24
pull Bot pushed a commit to ozbek/cmux that referenced this pull request Apr 26, 2026
> Mux worked on behalf of Mike.

## Summary

Absorbs the small path-app hardening pieces from coder#3184 on top of the
merged coder#3194 implementation. This keeps the existing server-side prefix
detection, direct prefixed route handling, Scalar rewriting, and
terminal popout path support intact.

## Background

coder#3194 made mux work under Coder path-app iframe URLs. coder#3184 had a few
defensive ideas worth keeping as a focused follow-up: tolerate slashless
app-root URLs, avoid stale service worker caches, and keep static shell
assets resolving when a proxy strips the app prefix without sending
forwarding headers.

## Implementation

- Uses the detected public base path when available, preserving absolute
prefixed base hrefs for Coder path-app requests.
- Falls back to a relative base href climb when no public prefix is
detected, so root hosting and stripped-prefix proxy paths both resolve
static assets from the app root.
- Injects a slashless app-root redirect script before the base tag only
for app-root shell responses, with a same-origin redirect target and a
double-slash path guard.
- Bumps the service worker cache name to `mux-v2` and precaches relative
shell URLs.
- Adds regression coverage for the exact Coder path shape with a token
query, for example `/@admin/<workspace>.main/apps/mux/?token=...`, plus
a double-slash redirect guard.

## Validation

- `bun test src/node/orpc/server.test.ts`
- `bun test src/common/appProxyBasePath.test.ts
src/browser/utils/backendBaseUrl.test.ts`
- `make static-check`

## Risks

Low to moderate. This touches SPA shell HTML generation, but keeps
detected Coder path-app base href behavior unchanged and adds tests for
relative deep-route fallback, slashless app-root handling, direct
prefixed requests, double-slash paths, and false-positive `/apps/`
paths.

---

_Generated with `mux` • Model: `openai:gpt-5.5` • Thinking: `xhigh` •
Cost: `$78.40`_

<!-- mux-attribution: model=openai:gpt-5.5 thinking=xhigh costs=78.40
-->
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant